You are on page 1of 84

Project Management and Scrum Software

Alexander Dymo
RailsConf 2009

Advanced Performance Optimization


of Rails Applications

http://en.oreilly.com/rails2009/public/schedule/detail/8615

www.acunote.com
What Am I Optimizing? Project Management and Scrum Software

http://en.oreilly.com/rails2009/public/schedule/detail/8615

Acunote www.acunote.com

Online project management and Scrum software


Ruby on Rails application since inception in 2006

● ~3700 customers
● Hosted on Engine Yard
● Hosted on Customers' Servers
● nginx + mongrel
● PostgreSQL
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 2 / 84
Performance Degradation Over Time Project Management and Scrum Software

300
Request Time (on development box), %

250 Actually Happens: O(nc)

Best Case: O(log n)


200

150

100
April 2008 May 2008 June 2008 July 2008

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 3 / 84


Solutions? Project Management and Scrum Software

Throw Some Hardware at it!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 4 / 84


Solutions? Project Management and Scrum Software

Performance Optimization!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 5 / 84


Project Management and Scrum Software

What to optimize?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 6 / 84


What To Optimize? Project Management and Scrum Software

Development?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 7 / 84


What To Optimize? Project Management and Scrum Software

Development

AND Production

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 8 / 84


Project Management and Scrum Software

How to optimize?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 9 / 84


How To Optimize? Project Management and Scrum Software

Three rules of
performance optimization

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 10 / 84


Three Rules Of Performance Optimization Project Management and Scrum Software

1. Measure!
...the universal experience of
programmers who have been using
measurement tools has been that
their intuitive guesses fail...
Knuth

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 11 / 84


Three Rules Of Performance Optimization Project Management and Scrum Software

2. Optimize only what's slow!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 12 / 84


Three Rules Of Performance Optimization Project Management and Scrum Software

3. Optimize for the user!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 13 / 84


Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 14 / 84
Optimizing Ruby: Date Class Project Management and Scrum Software

What's wrong with Date?


> puts Benchmark.realtime { 1000.times { Time.mktime(2009, 5, 6, 0, 0, 0) } }
0.005
> puts Benchmark.realtime { 1000.times { Date.civil(2009, 5, 6) } }
0.080

16x slower than Time! Why?


%self total self wait child calls name
7.23 0.66 0.18 0.00 0.48 18601 <Class::Rational>#reduce
6.83 0.27 0.17 0.00 0.10 5782 <Class::Date>#jd_to_civil
6.43 0.21 0.16 0.00 0.05 31528 Rational#initialize
5.62 0.23 0.14 0.00 0.09 18601 Integer#gcd

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 15 / 84


Optimizing Ruby: Date Class Project Management and Scrum Software

Fixing Date: Use C, Luke!

Date::Performance gem with Date partially rewritten in C


by Ryan Tomayko (with my patches in 0.4.7)
> puts Benchmark.realtime { 1000.times { Time.mktime(2009, 5, 6, 0, 0, 0) } }
0.005
> puts Benchmark.realtime { 1000.times { Date.civil(2009, 5, 6) } }
0.080

> require 'date/performance'


puts Benchmark.realtime { 1000.times { Date.civil(2009, 5, 6) } }
0.006

git clone git://github.com/rtomayko/date-performance.git


rake package:build
cd dist && gem install date-performance-0.4.7.gem
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 16 / 84
Optimizing Ruby: Date Class Project Management and Scrum Software

Real-world impact of Date::Performance:

Before: 0.95 sec


After: 0.65 sec
1.5x!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 17 / 84


Optimizing Ruby: Misc Project Management and Scrum Software

Use String::<< instead of String::+=


> long_string = "foo" * 100000
> Benchmark.realtime { long_string += "foo" }
in theory: 75x
0.0003
in practice: up to 70x
> Benchmark.realtime { long_string << "foo" }
0.000004

Avoid BigDecimal comparisons with strings and integers


> n = BigDecimal("4.5")
> Benchmark.realtime { 10000.times { n <=> 4.5 } } in theory: 4.5x
0.063 in practice: 1.15x
> Benchmark.realtime { 10000.times { n <=> BigDecimal("4.5") } }
0.014

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 18 / 84


Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 19 / 84
Optimizing Rails: Preloading Associations Project Management and Scrum Software

Preloading associations is always a good idea...


except when Rails can't do the job:

class Foo
belongs_to :bar
end

foos = Foo.find_by_sql('select * from foos inner join bar')


foos.first.bar #extra SQL query!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 20 / 84


Optimizing Rails: Preloading Associations Project Management and Scrum Software

Virtual Attributes plugin:


http://github.com/acunote/virtual_attributes/

class Bar
end
class Foo
belongs_to :bar
preloadable_association :bar
end

foos = Foo.find_by_sql('
select * from foos
left outer join
(select id as preloaded_bar_id,
name as preloaded_bar_name
from bars) as bars
on foos.bar_id = bars.preloaded_bar_id')
foos.first.bar #no extra SQL query!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 21 / 84


Optimizing Rails: String Callbacks Project Management and Scrum Software

What can be wrong with this code?

class Task < ActiveRecord::Base


before_save "some_check()"
end
...
100.times {
Task.create attributes
}

Kernel#binding is called to eval() the string callback


That will duplicate your execution context in memory!
More memory taken => More time for GC

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 22 / 84


Optimizing Rails: Partial Rendering Project Management and Scrum Software

Not too uncommon, right?

<% for object in objects %> #1000 times


<%= render :partial => 'object',
:locals => { :object => object } %>
<% end %>

We create 1000 View instances for each object here!


Why?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 23 / 84


Optimizing Rails: Partial Rendering Project Management and Scrum Software

Template inlining for the resque:

<% for object in objects %> #1000 times


<%= render :partial => 'object',
:locals => { :object => object },
:inline => true %>
<% end %>

list.rhtml

_object.rhtml _object.rhtml

_object.rhtml _object.rhtml
list.rhtml
_object.rhtml _object.rhtml

_object.rhtml _object.rhtml

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 24 / 84


Optimizing Rails: Partial Rendering Project Management and Scrum Software

Template Inliner plugin:


http://github.com/acunote/template_inliner/

Real world effect from template inlining:

Rendering of 300 objects, 5 partials for each object


without inlining: 0.89 sec
with inlining: 0.75 sec

1.2x
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 25 / 84
Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 26 / 84
Optimizing Database Project Management and Scrum Software

How to optimize PostgreSQL:


explain analyze
explain analyze
explain analyze
...

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 27 / 84


Optimizing Database: PostgreSQL Tips Project Management and Scrum Software

EXPLAIN ANALYZE explains everything, but...


... run it also for the "cold" database state!

Example: complex query which works on 230 000 rows and


does 9 subselects / joins:
cold state: 28 sec, hot state: 2.42 sec

Database server restart doesn't help


Need to clear disk cache:
sudo echo 3 | sudo tee /proc/sys/vm/drop_caches
(Linux)

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 28 / 84


Optimizing Database: PostgreSQL Tips Project Management and Scrum Software

Use any(array ()) instead of in()


to force subselect and avoid join
explain analyze select * from issues where id in (select issue_id from tags_issues);

QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------
Merge IN Join (actual time=0.096..576.704 rows=55363 loops=1)
Merge Cond: (issues.id = tags_issues.issue_id)
-> Index Scan using issues_pkey on issues (actual time=0.027..270.557 rows=229991 loops=1)
-> Index Scan using tags_issues_issue_id_key on tags_issues (actual time=0.051..73.903 rows=70052loops=1)
Total runtime: 605.274 ms

explain analyze select * from issues where id = any( array( (select issue_id from tags_issues) ) );

QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on issues (actual time=247.358..297.932 rows=55363 loops=1)
Recheck Cond: (id = ANY ($0))
InitPlan
-> Seq Scan on tags_issues (actual time=0.017..51.291 rows=70052 loops=1)
-> Bitmap Index Scan on issues_pkey (actual time=246.589..246.589 rows=70052 loops=1)
Index Cond: (id = ANY ($0))
Total runtime: 325.205 ms
2x!
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 29 / 84
Database Optimization: PostgreSQL Tips Project Management and Scrum Software

Push down conditions into subselects and joins


PostgreSQL often won't do that for you

select *,
Issues (select notes.author from notes
id serial where notes.issue_id = issues.id
name varchar ) as note_authors
org_id integer from issues
where org_id = 1

Notes
id serial select *,
name varchar (select notes.author from notes
issue_id integer where notes.issue_id = issues.id and org_id = 1
org_id integer ) as note_authors
from issues
where org_id = 1

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 30 / 84


What To Do? Project Management and Scrum Software

● Optimize For Development Box


– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Optimize For Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Optimize For The User
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 31 / 84
Alternative Ruby Project Management and Scrum Software

Everybody says
"JRuby and Ruby 1.9 are faster"

Is that true in production?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 32 / 84


Alternative Ruby Project Management and Scrum Software

In short, YES!

= Acunote Benchmarks = MRI JRuby 1.9.1


Date/Time Intensive Ops 1.23 0.58 0.53
Rendering Intensive Ops 0.61 0.44 0.30
Calculations Intensive Ops 2.57 1.79 1.33
Database Intensive Ops 5.58 4.63 3.29

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 33 / 84


Alternative Ruby Project Management and Scrum Software

In short, YES!

= Acunote Benchmarks = MRI JRuby 1.9.1


Date/Time Intensive Ops 1x 2.1x 2.3x
Rendering Intensive Ops 1x 1.4x 2.0x
Calculations Intensive Ops 1x 1.4x 1.9x
Database Intensive Ops 1x 1.2x 1.7x

JRuby: 1.5x faster


Ruby 1.9: 2.0x faster

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 34 / 84


Alternative Ruby Project Management and Scrum Software

What is faster?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 35 / 84


Alternative Ruby Project Management and Scrum Software

This is where all the improvement went:

Acunote Copy Tasks Benchmark MRI JRuby 1.9.1


Request Time 5.52 4.45 3.24
Template Rendering Time 0.53 0.21 0.21
Database Time 0.70 1.32 0.69
GC Time 1.07 N/A 0.62

Much faster template rendering!


Less GC!
JDBC database driver performance issue with JRuby?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 36 / 84


Alternative Ruby Project Management and Scrum Software

Why faster?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 37 / 84


Alternative Ruby Project Management and Scrum Software

Things I usually see in the profiler after optimizing:


%self self calls name
2.73 0.05 351 Range#each-1
2.73 0.05 33822 Hash#[]=
2.19 0.04 4 Acts::AdvancedTree::Tree#walk_tree
2.19 0.04 44076 Hash#[]
1.64 0.03 1966 Array#each-1
1.64 0.03 378 Org#pricing_plan
1.64 0.03 1743 Array#each
1.09 0.02 1688 ActiveRecord::AttributeMethods#respond_to?
1.09 0.02 1311 Hash#each
1.09 0.02 6180 ActiveRecord::AttributeMethods#read_attribute_before_typecast
1.09 0.02 13725 Fixnum#==
1.09 0.02 46736 Array#[]
1.09 0.02 15631 String#to_s
1.09 0.02 24330 String#concat
1.09 0.02 916 ActiveRecord::Associations#association_instance_get
1.09 0.02 242 ActionView::Helpers::NumberHelper#number_with_precision
1.09 0.02 7417 Fixnum#to_s

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 38 / 84


Alternative Ruby Project Management and Scrum Software

# of method calls during one request:


50 000 - Array
35 000 - Hash
25 000 - String

Slow classes written in Ruby:


Date
Rational

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 39 / 84


Alternative Ruby Project Management and Scrum Software

Alternative Rubys optimize mostly:


● the cost of function call
● complex computations in pure Ruby
● memory by not keeping source code AST

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 40 / 84


Alternative Ruby Project Management and Scrum Software

Alternative Rubys optimize mostly:


● the cost of function call
● complex computations in pure Ruby
● memory by not keeping source code AST

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 41 / 84


Alternative Ruby Project Management and Scrum Software

So, shall I use alternative Ruby?


Definitely Yes!... but

JRuby: if your application works with it


(run requests hundreds of times to check)
Ruby 1.9: if all gems you need are ported

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 42 / 84


Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 43 / 84
Optimizing For Shared Environment Project Management and Scrum Software

Issues we experienced deploying on Engine Yard:

1) VPS is just too damn slow


2) VPS may have too little memory to run the request!
3) shared database server is a problem
4) network filesystem may cause harm as well

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 44 / 84


Optimizing For Shared Environment Project Management and Scrum Software

VPS may have too little memory to run the request

Think 512M should be enough?


Think again.
We saw requests that took 1G of memory!

Solutions:
● buy more memory
● optimize memory
● set memory limits for mongrels (with monit)

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 45 / 84


Optimizing For Shared Environment Project Management and Scrum Software

You're competing for memory cache on a shared server:


1. two databases with equal load share the cache

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 46 / 84


Optimizing For Shared Environment Project Management and Scrum Software

You're competing for memory cache on a shared server:


2. one of the databases gets more load and wins the cache

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 47 / 84


Optimizing For Shared Environment Project Management and Scrum Software

As a result, your database can always be in a "cold" state


and you read data from disk, not from memory!

complex query which works on 230 000 rows and


does 9 subselects / joins:
from disk: 28 sec, from memory: 2.42 sec

Solutions:
optimize for the cold state
sudo echo 3 | sudo tee /proc/sys/vm/drop_caches
push down SQL conditions

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 48 / 84


Optimizing For Shared Environment Project Management and Scrum Software

fstat() is slow on network filesystem (GFS)

Request to render list of tasks in Acunote:


on development box: 0.50 sec
on production box: 0.50 - 2.50 sec

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 49 / 84


Optimizing For Shared Environment Project Management and Scrum Software

fstat() is slow on network filesystem (GFS)


Couldn't figure out why until we ran strace

We used
a) filesystem store for fragment caching
b) expire_fragment(regexp)

Later looked through all cache directories even though we knew


the cache is located in only one specific subdir

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 50 / 84


Optimizing For Shared Environment Project Management and Scrum Software

fstat() is slow on network filesystem (GFS)


Solution:
memcached instead of filesystem

if filesystem is ok, here's a trick:


http://blog.pluron.com/2008/07/hell-is-paved-w.html

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 51 / 84


Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 52 / 84
Live Debugging Project Management and Scrum Software

To see what's wrong on "live" application:


For Linux: strace and oprofile
For Mac and Solaris: dtrace
For Windows: uhm... about time to switch ;)

To monitor for known problems:


monit
nagios
own scripts to analyze application logs

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 53 / 84


Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 54 / 84
Load Balancing Project Management and Scrum Software

The problem of round-robin and fair load balancing

per-process queues

Rails App 1 1 2

Rails App 2 1 2 3

Rails App 3 1 2 3

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 55 / 84


Load Balancing Project Management and Scrum Software

Solution: the global queue

Rails App 1

Rails App 2 1 2 3 4 5

Rails App 3

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 56 / 84


Load Balancing Project Management and Scrum Software

Think you need mod_rails / Passenger for this?


You're right, but...

you can emulate this with nginx and mongrels

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 57 / 84


Load Balancing Project Management and Scrum Software

Dedicated queues for long-running requests

queue for long-running requests

Rails App 1 1

regular per-process queues

Rails App 2 1 2

Rails App 3 1 2 3

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 58 / 84


Load Balancing Project Management and Scrum Software

nginx configuration for dedicated queues


upstream mongrel {
server 127.0.0.1:5000;
server 127.0.0.1:5001;
}
upstream rss_mongrel {
server 127.0.0.1:5002;
}
server {
location / {
location ~ ^/feeds/(rss|atom) {
if (!-f $request_filename) {
proxy_pass http://rss_mongrel;
break;
}
}
if (!-f $request_filename) {
proxy_pass http://mongrel;
}
}
}
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 59 / 84
Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 60 / 84
Optimize For The User: HTTP Project Management and Scrum Software

Backend Network and Frontend


5% 95%

Things to consider:
● Gzip HTML, CSS and JS
● Minify JS
● Collect JS and CSS
(javascript_include_tag :all, :cache => true)
● Far future expires headers for JS, CSS, images
● Sprites
● Cache-Control: public
● everything else YSlow tells you

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 61 / 84


Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 62 / 84
Optimize Frontend: Javascript Project Management and Scrum Software

The worst enemy of every


web developer is...

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 63 / 84


Optimize Frontend: Javascript Project Management and Scrum Software

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 64 / 84


Optimize Frontend: Javascript Project Management and Scrum Software

Things you don't want to hear from your users:

"...Your server is slow..."

said the user after clicking


on the link to show a form
with plain javascript (no AJAX)

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 65 / 84


Optimize Frontend: Javascript Project Management and Scrum Software

Known hotspots in Javascript:


- eval()
- all DOM operations - avoid if possible, for example
- use element.className instead of element.readAttribute('class')
- use element.id instead of element.readAttirbute('id')
- $$() selectors, especially attribute selectors
- may be expensive, measure first
- $$('#some .listing td a.popup[accesslink]'
- use getElementsByTagName() and iterate results instead
- element.style.* changes
- change class instead
- $() and getElementById on large (~20000 elements) pages
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 66 / 84
Things To Optimize Project Management and Scrum Software

● Development
– Ruby code
– Rails code
– Database queries
– Alternative Ruby
● Production
– Shared filesystems and databases
– Live debugging
– Load balancing
● Frontend
– HTTP
– Javascript
– Internet Explorer
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 67 / 84
Optimize Frontend: IE Project Management and Scrum Software

Unsurprisingly...
IE is also slow!

Slow things that are especially slow in IE:


- $() and $$(), even on small pages
- getElementsByName()
- style switching

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 68 / 84


Optimize Frontend: IE Project Management and Scrum Software

Good things about IE:

profiler in IE8
fast in IE => fast everywhere else!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 69 / 84


Keep It Fast! Project Management and Scrum Software

So, you've optimized your application.


How to keep it fast?

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 70 / 84


Keep It Fast! Project Management and Scrum Software

Measure, measure and measure...


Use profiler
Optimize CPU and Memory

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 71 / 84


Keep It Fast: Measure Project Management and Scrum Software

Keep a set of benchmarks for most frequent user requests.


For example:
Benchmark Burndown 120 0.70 ± 0.00
Benchmark Inc. Burndown 120 0.92 ± 0.01
Benchmark Sprint 20 x (1+5) (C) 0.45 ± 0.00
Benchmark Issues 100 (C) 0.34 ± 0.00
Benchmark Prediction 120 0.56 ± 0.00
Benchmark Progress 120 0.23 ± 0.00
Benchmark Sprint 20 x (1+5) 0.93 ± 0.00
Benchmark Timeline 5x100 0.11 ± 0.00
Benchmark Signup 0.77 ± 0.00
Benchmark Export 0.20 ± 0.00
Benchmark Move Here 20/120 0.89 ± 0.00
Benchmark Order By User 0.98 ± 0.00
Benchmark Set Field (EP) 0.21 ± 0.00
Benchmark Task Create + Tag 0.23 ± 0.00
... 30 more ...

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 72 / 84


Keep It Fast: Measure Project Management and Scrum Software

Benchmarks as a special kind of tests:


class RenderingTest < ActionController::IntegrationTest

def test_sprint_rendering
login_with users(:user), "user"

benchmark :title => "Sprint 20 x (1+5) (C)",


:route => "projects/1/sprints/3/show",
:assert_template => "tasks/index"
end

end

Benchmark Sprint 20 x (1+5) (C) 0.45 ± 0.00

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 73 / 84


Keep It Fast: Measure Project Management and Scrum Software

Benchmarks as a special kind of tests:


def benchmark(options = {})
(0..100).each do |i|
GC.start
pid = fork do
begin
out = File.open("values", "a")
ActiveRecord::Base.transaction do
elapsed_time = Benchmark::realtime do
request_method = options[:post] ? :post : :get
send(request_method, options[:route])
end
out.puts elapsed_time if i > 0
out.close
raise CustomTransactionError
end
rescue CustomTransactionError
exit
end
end
Process::waitpid pid
ActiveRecord::Base.connection.reconnect!
end
values = File.read("values")
print "#{mean(values).to_02f} ± #{sigma(values).to_02f}\n"
end
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 74 / 84
Keep It Fast: Query Testing Project Management and Scrum Software

Losing 10ms in benchmark might seem OK

Except that it's sometimes not


because you're running one more SQL query

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 75 / 84


Keep It Fast: Query Testing Project Management and Scrum Software

def test_queries
queries = track_queries do
get :index
end
assert_equal queries, [
"Foo Load",
"Bar Load",
"Event Create"
]
end

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 76 / 84


Keep It Fast: Query Testing Project Management and Scrum Software

module ActiveSupport
class BufferedLogger

attr_reader :tracked_queries

def tracking=(val)
@tracked_queries = []
@tracking = val
end

def debug_with_tracking(message)
@tracked_queries << $1 if @tracking && message =~ /3[56]\;1m(.* (Load|Create|
Update|Destroy)) \(/
debug_without_tracking(message)
end
alias_method_chain :debug, :tracking

end
end

class ActiveSupport::TestCase
def track_queries(&block)
RAILS_DEFAULT_LOGGER.tracking = true
yield
result = RAILS_DEFAULT_LOGGER.tracked_queries
RAILS_DEFAULT_LOGGER.tracking = false
result
end
end

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 77 / 84


Keep It Fast: Use Profiler Project Management and Scrum Software

Profiler will always tell you what's wrong:


%self total self child calls name
8.39 0.54 0.23 0.31 602 Array#each_index
7.30 0.41 0.20 0.21 1227 Integer#gcd
6.20 0.49 0.17 0.32 5760 Timecell#date
5.11 0.15 0.14 0.01 1 Magick::Image#to_blob

gem install ruby-prof


use "performance" tests for Rails 2.x
KCachegrind to visualize the results
http://kcachegrind.sourceforge.net

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 78 / 84


Keep It Fast: Use Profiler Project Management and Scrum Software

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 79 / 84


Keep It Fast: Optimize CPU and Memory Project Management and Scrum Software

Memory profiler will explain the missing details:

Example benchmark: 5.52 sec request time, 1.07 sec GC time


Consumed memory: 55M

Ruby runs GC after allocating


8M memory or doing 10000 allocations

Simple math: 55 / 8 = 6 GC calls


Each GC call takes 0.18 sec!

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 80 / 84


Keep It Fast: Optimize CPU and Memory Project Management and Scrum Software

How to use memory profiler:

Recompile Ruby with GC patch


http://www.acunote.com/system/ruby186-p287-gc.patch

Reinstall ruby-prof

Use RUBY_PROF_MEASURE_MODE=memory
when running ruby-prof

http://blog.pluron.com/2008/02/memory-profilin.html
Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 81 / 84
Remember! Project Management and Scrum Software

Measure, measure, measure... (with ruby-prof)


Optimize only what's slow
Optimize not only CPU, but memory
Optimize for user experience
Keep a set of performance regression tests
Monitor performance

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 82 / 84


Help Us Make Ruby and Rails Faster! Project Management and Scrum Software

We need your help!

● we'd like to see GC patch applied to mainstream Ruby


(to profile memory without recompiling Ruby)
● we'd like to see people porting their gems to Ruby 1.9
● we'd like to see more real-world benchmarks

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 83 / 84


Project Management and Scrum Software

Thanks!
Slides for this talk:
http://en.oreilly.com/rails2009/public/schedule/detail/8615

Rails performance articles and more:


http://blog.pluron.com

Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009 84 / 84

You might also like